/*
 * NetworkManager.cpp
 *
 *  Created on: Dec 11, 2014
 *      Author: toti
 */

#include <Network/IBRCNetworkManager.hpp>

IBRCNetworkManager::IBRCNetworkManager(ConnectionManager *connectionManager) {

	this->connectionManager = connectionManager;

	ibrcNetworkManagerState = STATE_RUNNING;

	networkServerList = new std::list<NetworkServer*>();
	networkClientList = new std::list<NetworkClient*>();
	networkChannelList = new std::list<NetworkChannel*>();

	networkClient = 0;

	networkServer = new NetworkServer(connectionManager->getUserInterface(), Config::instance()->getClientName());
	networkServerList->push_back(networkServer);

}

void IBRCNetworkManager::processData() {

	MessagePackage* inboundMessagePackage = 0;

	/* Get incoming MessagePackage from inbound fifo */
	while ((inboundMessagePackage = connectionManager->popInboundMessagePackageList())) {

		/* Process MessagePackage depending on message type */
		switch (inboundMessagePackage->getMessagePackageType()) {

		case MessagePackage::CLIENT_MESSAGE_PACKAGE:

			/* Message from connected client */
			processClientMessagePackage((ClientMessagePackage*) inboundMessagePackage);
			break;

		case MessagePackage::SERVER_MESSAGE_PACKAGE:

			/* Message from connected server */
			processServerMessagePackage((ServerMessagePackage*) inboundMessagePackage);
			break;

		}

		delete (inboundMessagePackage);

	}

}

/***********************************************************************************
 ***********************************************************************************
 *
 * Server Handler:
 *
 * 	Handler functions for server messages
 *
 *
 *
 ***********************************************************************************
 ***********************************************************************************/

void IBRCNetworkManager::processServerMessagePackage(ServerMessagePackage *serverMessagePackage) {

	/*
	 * Process ServerMessagePackage dependent on it's type
	 */
	switch (serverMessagePackage->getMessagePackageType()) {

	case ServerMessagePackage::TYPE_SERVER_REGISTRATION:

		processServerRegistration(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_SERVER_UNREGISTRATION:

		processServerUnregistration(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_SERVER_RECONNECT:

		processServerReconnect(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_UPDATE_CLIENT_LIST:

		processServerUpdateClientList(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_UPDATE_CHANNEL_LIST:

		processServerUpdateChannelList(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_UPDATEREQUEST_SERVER_LIST:

		processServerUpdateRequestServerList(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_UPDATEREQUEST_CLIENT_LIST:

		processServerUpdateRequestClientList(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_UPDATEREQUEST_CHANNEL_LIST:

		processServerUpdateRequestChannelList(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_RESPONSE:

		processServerResponse(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_CHANNEL_MESSAGE:

		processServerChannelMessage(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_CHANNEL_PRIVMESSAGE:

		processServerChannelPrivateMessage(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_REQUEST_JOIN:

		processServerRequestJoin(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_REQUEST_LEAVE:

		processServerRequestLeave(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_REQUEST_NICK:

		processServerRequestNick(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_REQUEST_QUIT:

		processServerRequestQuit(serverMessagePackage);
		break;

	case ServerMessagePackage::TYPE_REQUEST_SETTOPIC:

		processServerRequestSettopic(serverMessagePackage);
		break;

	}
}

void IBRCNetworkManager::processServerRegistration(ServerMessagePackage *package) {

	/* Get LocalNode of sender server */
	LocalServer *localServer = (LocalServer*) package->getLocalNode();

	std::string serverName = package->getParameter(ServerMessagePackage::SERVER_REGISTRATION__SERVER_NAME);

	NetworkServer *networkServer = findNetworkServerByName(serverName);

	if (networkServer == 0) {

		networkServer = new NetworkServer(localServer, serverName);
		networkServerList->push_back(networkServer);

	} else {

		networkServer->setLocalNode(localServer);

	}

	performForwardBroadcast(package);

}

void IBRCNetworkManager::processServerUnregistration(ServerMessagePackage *package) {

	std::string serverName = package->getParameter(ServerMessagePackage::SERVER_UNREGISTRATION__SERVER_NAME);

	NetworkServer *networkServer = findNetworkServerByName(serverName);
	NetworkClient *networkClient = findNetworkClientByServer(networkServer);
	NetworkChannel *networkChannel = networkClient->getNetworkChannel();

	networkClientList->remove(networkClient);
	delete (networkClient);

	networkServerList->remove(networkServer);
	delete (networkServer);

	/*
	 * Set the channel owner of all channel to 0 where we were owner
	 */
	std::list<NetworkChannel*> channelOwnerList;
	findNetworkChannelByOwner(&channelOwnerList, networkClient);

	for(std::list<NetworkChannel*>::iterator it=channelOwnerList.begin();it!=channelOwnerList.end();++it){

		NetworkChannel *networkChannel = (*it);
		networkChannel->setChannelOwner(0);

	}

	/*
	 * Check if any channel is orphaned after we left and delete it
	 */
	std::list<NetworkClient*> channelMemberList;
	findNetworkClientsByChannel(&channelMemberList, networkChannel);

	if (channelMemberList.size() == 0) {

		networkChannelList->remove(networkChannel);
		delete (networkChannel);

	}

	performForwardBroadcast(package);

}

void IBRCNetworkManager::processServerReconnect(ServerMessagePackage *package) {

	std::string serverAddress = package->getParameter(ServerMessagePackage::SERVER_RECONNECT__SERVER_IP);
	std::string serverPortString = package->getParameter(ServerMessagePackage::SERVER_RECONNECT__SERVER_PORT);

	if(!serverAddress.compare("discover")){

		sleep(2);
		connectionManager->setLocalParentServer(0);
		connectionManager->getPeerDiscovery()->sendPeerDiscoveryRequest();

		std::cout << Config::instance()->getClientName();
		std::cout << ": Received command: discover new peer!" << std::endl;

	}else if(!serverAddress.compare("nothing")){

		// We are the root node now
		connectionManager->setLocalParentServer(0);

		std::cout << Config::instance()->getClientName();
		std::cout << ": Received command: You're the new root!" << std::endl;

	}else{

		std::cout << Config::instance()->getClientName();
		std::cout << ": Received command: Reconnect to parent " << serverAddress << " " << serverPortString << std::endl;
		while (!connectionManager->openParentServerConnection(serverPortString, serverAddress)) {
			sleep(1);
		}

	}
}

void IBRCNetworkManager::processServerUpdateClientList(ServerMessagePackage *package) {

	std::string currentNick = package->getParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__NICK);
	std::string remove = package->getParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__REMOVE);
	std::string server = package->getParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__SERVER);
	std::string newNick = package->getParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__NEW_NICK);
	std::string channel = package->getParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__CHANNEL);

	NetworkClient *networkClient = findNetworkClientByName(currentNick);
	NetworkServer *networkServer = findNetworkServerByName(server);
	NetworkChannel *networkChannel = findNetworkChannelByName(channel);

	if (networkClient == 0) {
		networkClient = new NetworkClient();
		networkClientList->push_back(networkClient);
	}

	if (!remove.compare("true")) {

		networkClientList->remove(networkClient);
		delete (networkClient);

	} else {

		networkClient->setClientName(newNick);
		networkClient->setNetworkServer(networkServer);
		networkClient->setNetworkChannel(networkChannel);

	}

	performForwardBroadcast(package);

}

void IBRCNetworkManager::processServerUpdateChannelList(ServerMessagePackage *package) {

	std::string channel = package->getParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__CHANNEL);
	std::string remove = package->getParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__REMOVE);
	std::string owner = package->getParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__OWNER);
	std::string topic = package->getParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__TOPIC);

	NetworkChannel *networkChannel = findNetworkChannelByName(channel);
	NetworkClient *networkClient = findNetworkClientByName(owner);

	if (networkChannel == 0) {

		networkChannel = new NetworkChannel();

		networkChannel->setChannelName(channel);
		networkChannel->setChannelOwner(networkClient);
		networkChannel->setTopic(topic);

		networkChannelList->push_back(networkChannel);

	} else {

		networkChannel->setChannelOwner(networkClient);
		networkChannel->setTopic(topic);

		if (!remove.compare("true")) {

			networkChannelList->remove(networkChannel);
			delete (networkChannel);

		}

	}

	performForwardBroadcast(package);

}

void IBRCNetworkManager::processServerUpdateRequestServerList(ServerMessagePackage *package) {

	/* Get LocalNode of sender server */
	LocalServer *localServer = (LocalServer*) package->getLocalNode();

	/* Deploy our server list */
	for (std::list<NetworkServer*>::iterator it = networkServerList->begin(); it != networkServerList->end(); ++it) {

		if (networkServer->getLocalNode() != localServer) {

			NetworkServer *networkServer = *it;

			ServerMessagePackage *outboundPackage = new ServerMessagePackage(localServer);
			outboundPackage->setMessageType(ServerMessagePackage::TYPE_SERVER_REGISTRATION);
			outboundPackage->setSender(this->networkServer->getServerName());
			outboundPackage->setParameter(ServerMessagePackage::SERVER_REGISTRATION__SERVER_NAME, networkServer->getServerName());

			connectionManager->pushOutboundMessagePackageList(outboundPackage);

		}
	}

}

void IBRCNetworkManager::processServerUpdateRequestClientList(ServerMessagePackage *package) {

	/* Get LocalNode of sender server */
	LocalServer *localServer = (LocalServer*) package->getLocalNode();

	/* Deploy our server list */
	for (std::list<NetworkClient*>::iterator it = networkClientList->begin(); it != networkClientList->end(); ++it) {

		NetworkClient *networkClient = *it;
		NetworkChannel *networkChannel = networkClient->getNetworkChannel();

		std::string nick = networkClient->getClientName();
		std::string serverName = networkClient->getNetworkServer()->getServerName();
		std::string channelName;
		if (networkChannel) {
			channelName = networkChannel->getChannelName();
		} else {
			channelName = "none";
		}

		ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(localServer);
		serverMessagePackage->setSender(this->networkServer->getServerName());
		serverMessagePackage->setMessageType(ServerMessagePackage::TYPE_UPDATE_CLIENT_LIST);
		serverMessagePackage->setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__NICK, nick);
		serverMessagePackage->setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__SERVER, serverName);
		serverMessagePackage->setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__NEW_NICK, nick);
		serverMessagePackage->setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__CHANNEL, channelName);

		connectionManager->pushOutboundMessagePackageList(serverMessagePackage);

	}

}

void IBRCNetworkManager::processServerUpdateRequestChannelList(ServerMessagePackage *package) {

	/* Get LocalNode of sender server */
	LocalServer *localServer = (LocalServer*) package->getLocalNode();

	/* Deploy our server list */
	for (std::list<NetworkChannel*>::iterator it = networkChannelList->begin(); it != networkChannelList->end(); ++it) {

		NetworkChannel *networkChannel = *it;
		NetworkClient *networkClient = networkChannel->getChannelOwner();

		std::string channelName = networkChannel->getChannelName();
		std::string topic = networkChannel->getTopic();
		std::string ownerName;
		if(networkClient){
			ownerName = networkClient->getClientName();
		}else{
			ownerName = "none";
		}

		ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(localServer);
		serverMessagePackage->setSender(this->networkServer->getServerName());
		serverMessagePackage->setMessageType(ServerMessagePackage::TYPE_UPDATE_CHANNEL_LIST);
		serverMessagePackage->setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__CHANNEL, channelName);
		serverMessagePackage->setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__OWNER, ownerName);
		serverMessagePackage->setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__REMOVE, "false");
		serverMessagePackage->setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__TOPIC, topic);

		connectionManager->pushOutboundMessagePackageList(serverMessagePackage);

	}

}

void IBRCNetworkManager::processServerResponse(ServerMessagePackage *package) {

	std::string receiver = package->getParameter(ServerMessagePackage::RESPONSE__RECEIVER);

	if (!receiver.compare(Config::instance()->getClientName())) {

		/*
		 * Inform localClient
		 */
		LocalNode *localClient = connectionManager->getUserInterface();
		std::string& clientText = package->getParameter(ServerMessagePackage::RESPONSE__MESSAGE);

		ClientMessagePackage *clientMessagePackage = new ClientMessagePackage(localClient);
		clientMessagePackage->setText(clientText);

		connectionManager->pushOutboundMessagePackageList(clientMessagePackage);

	} else {
		performForwardBroadcast(package);
	}

}

void IBRCNetworkManager::processServerChannelMessage(ServerMessagePackage *package) {

	/* Get required information from ServerMessagePackage */
	std::string& senderServerName = package->getSender();
	std::string& channelName = package->getParameter(ServerMessagePackage::CHANNEL_MESSAGE__NAME);
	std::string& senderName = package->getParameter(ServerMessagePackage::CHANNEL_MESSAGE__SENDER);
	std::string& text = package->getText();

	std::list<NetworkClient*> networkClientList;
	std::list<NetworkServer*> networkServerList;
	std::list<LocalServer*> localServerList;

	/* Get NetworkServer of sender */
	NetworkServer *senderServer = findNetworkServerByName(senderServerName);
	/* Get NetworkChannel of sender */
	NetworkChannel *networkChannel = findNetworkChannelByName(channelName);
	/* Get all NetworkClients assigned to that channel */
	findNetworkClientsByChannel(&networkClientList, networkChannel);
	/* Get all NetworkServer of the NetworkClients to send the message to */
	findNetworkServerByClients(&networkServerList, &networkClientList);
	/* Get all LocalNodes of the NetworkServer to populate the message */
	findLocalServerByNetworkServer(&localServerList, &networkServerList);

	/*
	 * Send the MessagePackage to all localClients except the one we received it from
	 */
	for (std::list<LocalServer*>::iterator it = localServerList.begin(); it != localServerList.end(); ++it) {

		LocalServer *localServer = *it;

		if (localServer && localServer != senderServer->getLocalNode()) {

			ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(localServer);
			*serverMessagePackage = *package;
			serverMessagePackage->setLocalNode(localServer);

			connectionManager->pushOutboundMessagePackageList(serverMessagePackage);

		}

	}

	/*
	 * Send the Message to all clients connected to our server and assigned to the channel
	 */
	std::list<LocalNode*> *localClientList = connectionManager->getLocalClientList();

	for (std::list<LocalNode*>::iterator it = localClientList->begin(); it != localClientList->end(); ++it) {

		LocalClient *localClient = (LocalClient*) *it;

		if (localClient->getNetworkClient()->getNetworkChannel() == networkChannel) {

			/*
			 * Inform localClient
			 */
			std::string clientText;
			std::ostringstream clientTextStream;
			clientTextStream << senderName << ":> " << text;
			clientText = clientTextStream.str();

			ClientMessagePackage *clientMessagePackage = new ClientMessagePackage(localClient);
			clientMessagePackage->setText(clientText);

			connectionManager->pushOutboundMessagePackageList(clientMessagePackage);

		}

	}

}

void IBRCNetworkManager::processServerChannelPrivateMessage(ServerMessagePackage *package) {

	/* Get required information from ServerMessagePackage */
	std::string& destinationName = package->getParameter(ServerMessagePackage::CHANNEL_PRIVMESSAGE__NAME);
	std::string& senderName = package->getParameter(ServerMessagePackage::CHANNEL_PRIVMESSAGE__SENDER);
	std::string& text = package->getText();

	/* Get destination NetworkClient and NetworkServer */
	NetworkClient *destinationNetworkClient = findNetworkClientByName(destinationName);
	NetworkServer *destinationNetworkServer = destinationNetworkClient->getNetworkServer();

	/*
	 * Distinguish between locally connected and remote connected client
	 */
	if (destinationNetworkServer == networkServer) {

		/*
		 * Destination client is connected to this server
		 */
		std::list<LocalNode*> *localClientList = connectionManager->getLocalClientList();

		for (std::list<LocalNode*>::iterator it = localClientList->begin(); it != localClientList->end(); ++it) {

			LocalClient *localClient = (LocalClient*) *it;

			if (localClient->getNetworkClient() == destinationNetworkClient) {

				/*
				 * Inform localClient
				 */
				std::string clientText;
				std::ostringstream clientTextStream;
				clientTextStream << senderName << " PRIVAT:> " << text;
				clientText = clientTextStream.str();

				ClientMessagePackage *clientMessagePackage = new ClientMessagePackage(localClient);
				clientMessagePackage->setText(clientText);

				connectionManager->pushOutboundMessagePackageList(clientMessagePackage);

			}

		}

	} else {

		/*
		 * Destination client is connected to a different server than this one,
		 * forward message into destination clients server direction
		 */

		LocalServer *localServer = (LocalServer*) destinationNetworkServer->getLocalNode();

		ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(localServer);
		*serverMessagePackage = *package;
		serverMessagePackage->setLocalNode(localServer);

		connectionManager->pushOutboundMessagePackageList(serverMessagePackage);

	}

}

void IBRCNetworkManager::processServerRequestSettopic(ServerMessagePackage *package) {

	LocalNode *parentNode = connectionManager->getLocalParentServer();

	if (parentNode) {

		ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(parentNode);
		*serverMessagePackage = *package;
		serverMessagePackage->setLocalNode(parentNode);

		connectionManager->pushOutboundMessagePackageList(serverMessagePackage);
	} else {

		std::string channel = package->getParameter(ServerMessagePackage::REQUEST_SETTOPIC__CHANNEL);
		std::string nick = package->getParameter(ServerMessagePackage::REQUEST_SETTOPIC__NICK);
		std::string topic = package->getParameter(ServerMessagePackage::REQUEST_SETTOPIC__TOPIC);

		NetworkChannel *networkChannel = findNetworkChannelByName(channel);
		NetworkClient *networkClient = findNetworkClientByName(nick);

		if (networkChannel->getChannelOwner() == networkClient) {

			networkChannel->setTopic(topic);

			ServerMessagePackage serverMessagePackage(0);
			serverMessagePackage.setMessageType(ServerMessagePackage::TYPE_UPDATE_CHANNEL_LIST);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__CHANNEL, channel);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__REMOVE, "false");
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__OWNER, nick);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__TOPIC, topic);
			performCompleteBroadcast(&serverMessagePackage);

		} else {

			ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(0);
			serverMessagePackage->setMessageType(ServerMessagePackage::TYPE_RESPONSE);
			serverMessagePackage->setParameter(ServerMessagePackage::RESPONSE__RECEIVER, nick);
			serverMessagePackage->setParameter(ServerMessagePackage::RESPONSE__MESSAGE, "NETWORK:> Error - permission denied!");

			connectionManager->pushOutboundMessagePackageList(serverMessagePackage);

		}

	}

}

void IBRCNetworkManager::processServerRequestJoin(ServerMessagePackage *package) {

	LocalNode *parentNode = connectionManager->getLocalParentServer();

	if (parentNode) {

		ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(parentNode);
		*serverMessagePackage = *package;
		serverMessagePackage->setLocalNode(parentNode);

		connectionManager->pushOutboundMessagePackageList(serverMessagePackage);
	} else {

		std::string nick = package->getParameter(ServerMessagePackage::REQUEST_JOIN__NICK);
		std::string channel = package->getParameter(ServerMessagePackage::REQUEST_JOIN__CHANNEL);
		std::string topic("none");

		NetworkClient *networkClient = findNetworkClientByName(nick);
		NetworkChannel *oldNetworkChannel = networkClient->getNetworkChannel();
		NetworkChannel *newNetworkChannel = findNetworkChannelByName(channel);
		NetworkServer *networkServer = networkClient->getNetworkServer();

		/*
		 * Create new channel if it doesn't exist yet
		 */
		if (newNetworkChannel == 0) {

			newNetworkChannel = new NetworkChannel();
			newNetworkChannel->setChannelName(channel);
			newNetworkChannel->setChannelOwner(networkClient);
			newNetworkChannel->setTopic(topic);

			networkChannelList->push_back(newNetworkChannel);

			ServerMessagePackage serverMessagePackage(0);
			serverMessagePackage.setMessageType(ServerMessagePackage::TYPE_UPDATE_CHANNEL_LIST);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__CHANNEL, channel);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__REMOVE, "false");
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__OWNER, nick);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__TOPIC, "none");

			performCompleteBroadcast(&serverMessagePackage);
		}

		/*
		 * Update channel of NetworkClient
		 */
		networkClient->setNetworkChannel(newNetworkChannel);

		ServerMessagePackage serverMessagePackage(0);
		serverMessagePackage.setMessageType(ServerMessagePackage::TYPE_UPDATE_CLIENT_LIST);
		serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__NICK, nick);
		serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__REMOVE, "false");
		serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__SERVER, networkServer->getServerName());
		serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__NEW_NICK, nick);
		serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__CHANNEL, channel);

		performCompleteBroadcast(&serverMessagePackage);

		if (oldNetworkChannel) {
			/*
			 * Remove old channel if empty
			 */
			std::list<NetworkClient*> oldNetworkChannelClientsList;
			findNetworkClientsByChannel(&oldNetworkChannelClientsList, oldNetworkChannel);

			if (oldNetworkChannelClientsList.size() == 0) {

				ServerMessagePackage serverMessagePackage(0);
				serverMessagePackage.setMessageType(ServerMessagePackage::TYPE_UPDATE_CHANNEL_LIST);
				serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__CHANNEL, oldNetworkChannel->getChannelName());
				serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__REMOVE, "true");
				serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__OWNER, "foo");
				serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__TOPIC, "foo");

				performCompleteBroadcast(&serverMessagePackage);

				networkChannelList->remove(oldNetworkChannel);
				delete (oldNetworkChannel);

			}
		}

	}
}

void IBRCNetworkManager::processServerRequestLeave(ServerMessagePackage *package) {

	LocalNode *parentNode = connectionManager->getLocalParentServer();

	if (parentNode) {

		ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(parentNode);
		*serverMessagePackage = *package;
		serverMessagePackage->setLocalNode(parentNode);

		connectionManager->pushOutboundMessagePackageList(serverMessagePackage);
	} else {

		std::string nick = package->getParameter(ServerMessagePackage::REQUEST_LEAVE__NICK);

		NetworkClient *networkClient = findNetworkClientByName(nick);
		NetworkChannel *oldNetworkChannel = networkClient->getNetworkChannel();
		NetworkServer *networkServer = networkClient->getNetworkServer();

		networkClient->setNetworkChannel(0);

		/*
		 * Update channel of NetworkClient
		 */
		ServerMessagePackage serverMessagePackage(0);
		serverMessagePackage.setMessageType(ServerMessagePackage::TYPE_UPDATE_CLIENT_LIST);
		serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__NICK, nick);
		serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__SERVER, networkServer->getServerName());
		serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__NEW_NICK, nick);
		serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__CHANNEL, "none");

		performCompleteBroadcast(&serverMessagePackage);

		/*
		 * Remove old channel if empty
		 */
		std::list<NetworkClient*> oldNetworkChannelClientsList;
		findNetworkClientsByChannel(&oldNetworkChannelClientsList, oldNetworkChannel);

		if (oldNetworkChannelClientsList.size() == 0) {

			std::string channelName = oldNetworkChannel->getChannelName();

			ServerMessagePackage serverMessagePackage(0);
			serverMessagePackage.setMessageType(ServerMessagePackage::TYPE_UPDATE_CHANNEL_LIST);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__CHANNEL, channelName);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__REMOVE, "true");
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__OWNER, "foo");
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__TOPIC, "foo");

			performCompleteBroadcast(&serverMessagePackage);

			networkChannelList->remove(oldNetworkChannel);
			delete (oldNetworkChannel);

		}

	}

}

void IBRCNetworkManager::processServerRequestNick(ServerMessagePackage *package) {

	LocalNode *parentNode = connectionManager->getLocalParentServer();

	if (parentNode) {

		ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(parentNode);
		*serverMessagePackage = *package;
		serverMessagePackage->setLocalNode(parentNode);

		connectionManager->pushOutboundMessagePackageList(serverMessagePackage);
	} else {

		std::string nick = package->getParameter(ServerMessagePackage::REQUEST_NICK__NICK);
		std::string newNick = package->getParameter(ServerMessagePackage::REQUEST_NICK__NEW_NICK);

		NetworkClient *oldNetworkClient = findNetworkClientByName(nick);
		NetworkClient *newNetworkClient = findNetworkClientByName(newNick);

		ServerMessagePackage serverMessagePackage(0);

		if (!newNick.compare("none")) {

			/*
			 * none is not a valid nick
			 */
			serverMessagePackage.setMessageType(ServerMessagePackage::TYPE_RESPONSE);
			serverMessagePackage.setParameter(ServerMessagePackage::RESPONSE__RECEIVER, nick);
			serverMessagePackage.setParameter(ServerMessagePackage::RESPONSE__MESSAGE, "NETWORK:> Error - nickname invalid!");

			performCompleteBroadcast(&serverMessagePackage);

		} else if (oldNetworkClient == 0) {

			NetworkServer *networkServer = findNetworkServerByName(package->getSender());

			/*
			 * New client
			 */
			newNetworkClient = new NetworkClient();
			newNetworkClient->setClientName(newNick);
			newNetworkClient->setNetworkChannel(0);
			newNetworkClient->setNetworkServer(networkServer);

			networkClientList->push_back(newNetworkClient);

			serverMessagePackage.setMessageType(ServerMessagePackage::TYPE_UPDATE_CLIENT_LIST);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__NICK, nick);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__SERVER, package->getSender());
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__NEW_NICK, newNick);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__CHANNEL, "none");

			performCompleteBroadcast(&serverMessagePackage);

		} else if (newNetworkClient == 0) {

			/*
			 * New nickname is valid
			 */
			NetworkServer *networkServer = oldNetworkClient->getNetworkServer();
			NetworkChannel *networkChannel = oldNetworkClient->getNetworkChannel();
			oldNetworkClient->setClientName(newNick);

			/* Update list of NetworkClients */
			serverMessagePackage.setMessageType(ServerMessagePackage::TYPE_UPDATE_CLIENT_LIST);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__NICK, nick);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__SERVER, networkServer->getServerName());
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__NEW_NICK, newNick);
			if (networkChannel) {
				serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__CHANNEL, networkChannel->getChannelName());
			} else {
				serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__CHANNEL, "none");
			}
			performCompleteBroadcast(&serverMessagePackage);

			serverMessagePackage.setMessageType(ServerMessagePackage::TYPE_RESPONSE);
			serverMessagePackage.setParameter(ServerMessagePackage::RESPONSE__RECEIVER, newNick);
			serverMessagePackage.setParameter(ServerMessagePackage::RESPONSE__MESSAGE, "NETWORK:> Confirmed - Assigned new nickname!");

			performCompleteBroadcast(&serverMessagePackage);

		} else {

			serverMessagePackage.setMessageType(ServerMessagePackage::TYPE_RESPONSE);
			serverMessagePackage.setParameter(ServerMessagePackage::RESPONSE__RECEIVER, nick);
			serverMessagePackage.setParameter(ServerMessagePackage::RESPONSE__MESSAGE, "NETWORK:> Error - nickname already exists!");

			performCompleteBroadcast(&serverMessagePackage);

		}

	}

}

void IBRCNetworkManager::processServerRequestQuit(ServerMessagePackage *package) {

	LocalNode *parentNode = connectionManager->getLocalParentServer();

	if (parentNode) {

		ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(parentNode);
		*serverMessagePackage = *package;
		serverMessagePackage->setLocalNode(parentNode);

		connectionManager->pushOutboundMessagePackageList(serverMessagePackage);
	} else {

		std::string nick = package->getParameter(ServerMessagePackage::REQUEST_QUIT__NICK);

		NetworkClient *networkClient = findNetworkClientByName(nick);
		NetworkChannel *oldNetworkChannel = networkClient->getNetworkChannel();
		NetworkServer *networkServer = networkClient->getNetworkServer();

		networkClientList->remove(networkClient);
		delete (networkClient);

		/*
		 * Update channel of NetworkClient
		 */
		ServerMessagePackage serverMessagePackage(0);
		serverMessagePackage.setMessageType(ServerMessagePackage::TYPE_UPDATE_CLIENT_LIST);
		serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__NICK, nick);
		serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__REMOVE, "true");
		serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__SERVER, networkServer->getServerName());
		serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__NEW_NICK, "none");
		serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CLIENT_LIST__CHANNEL, "none");

		performCompleteBroadcast(&serverMessagePackage);

		/*
		 * Remove old channel if empty
		 */
		std::list<NetworkClient*> oldNetworkChannelClientsList;
		findNetworkClientsByChannel(&oldNetworkChannelClientsList, oldNetworkChannel);

		if (oldNetworkChannelClientsList.size() == 0) {

			std::string channelName = oldNetworkChannel->getChannelName();

			ServerMessagePackage serverMessagePackage(0);
			serverMessagePackage.setMessageType(ServerMessagePackage::TYPE_UPDATE_CHANNEL_LIST);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__CHANNEL, channelName);
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__REMOVE, "true");
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__OWNER, "foo");
			serverMessagePackage.setParameter(ServerMessagePackage::UPDATE_CHANNEL_LIST__TOPIC, "foo");

			performCompleteBroadcast(&serverMessagePackage);

			networkChannelList->remove(oldNetworkChannel);
			delete (oldNetworkChannel);

		}

	}

}

/***********************************************************************************
 ***********************************************************************************
 *
 * Client Handler:
 *
 * 	Handler functions for client messages
 *
 *
 *
 ***********************************************************************************
 ***********************************************************************************/

void IBRCNetworkManager::processClientMessagePackage(ClientMessagePackage *clientMessagePackage) {

	/*
	 * Process ClientMessagePackage dependent on it's type
	 */
	switch (clientMessagePackage->getMessagePackageType()) {

	case ClientMessagePackage::TYPE_REGISTRATION:

		processClientRegistrationMessage(clientMessagePackage);
		break;

	case ClientMessagePackage::TYPE_CHANNEL_MESSAGE:

		processClientChannelMessage(clientMessagePackage);
		break;

	case ClientMessagePackage::TYPE_GETTOPIC_MESSAGE:

		processClientGettopicMessage(clientMessagePackage);
		break;

	case ClientMessagePackage::TYPE_JOIN_MESSAGE:

		processClientJoinMessage(clientMessagePackage);
		break;

	case ClientMessagePackage::TYPE_LEAVE_MESSAGE:

		processClientLeaveMessage(clientMessagePackage);
		break;

	case ClientMessagePackage::TYPE_LIST_MESSAGE:

		processClientListMessage(clientMessagePackage);
		break;

	case ClientMessagePackage::TYPE_NICK_MESSAGE:

		processClientNickMessage(clientMessagePackage);
		break;

	case ClientMessagePackage::TYPE_PRIVATE_MESSAGE:

		processClientPrivateMessage(clientMessagePackage);
		break;

	case ClientMessagePackage::TYPE_QUIT_MESSAGE:

		processClientQuitMessage(clientMessagePackage);
		break;

	case ClientMessagePackage::TYPE_SETTOPIC_MESSAGE:

		processClientSettopicMessage(clientMessagePackage);
		break;

	case ClientMessagePackage::TYPE_UNDEFINED_MESSAGE:

		break;

	}

}

void IBRCNetworkManager::processClientRegistrationMessage(ClientMessagePackage *package) {

	/* Get LocalClient of sender */
	LocalClient *localClient = (LocalClient*) package->getLocalNode();

	networkClient = new NetworkClient();
	networkClient->setClientName(Config::instance()->getClientName());
	networkClient->setNetworkServer(networkServer);

	localClient->setNetworkClient(networkClient);

	networkClientList->push_back(networkClient);

	/* Send welcome message to client */
	std::string clientText = "NETWORK:> Welcome!";
	ClientMessagePackage *clientMessagePackage = new ClientMessagePackage(localClient);
	clientMessagePackage->setText(clientText);

	connectionManager->pushOutboundMessagePackageList(clientMessagePackage);

}

void IBRCNetworkManager::processClientChannelMessage(ClientMessagePackage *package) {

	/* Get required information from ClientMessagePackage */
	LocalClient *localClient = (LocalClient*) package->getLocalNode();
	NetworkClient *networkClient = localClient->getNetworkClient();
	NetworkChannel *networkChannel = networkClient->getNetworkChannel();

	/*
	 * Check if client is assigned to a channel
	 */
	if (networkChannel) {

		/*
		 * Client is assigned to a channel
		 */
		std::string& senderName = networkClient->getClientName();
		std::string& channelName = networkChannel->getChannelName();
		std::string& text = package->getText();

		std::list<NetworkClient*> networkClientList;
		std::list<NetworkServer*> networkServerList;
		std::list<LocalServer*> localServerList;

		/* Get all NetworkClients connected to the channel */
		findNetworkClientsByChannel(&networkClientList, networkChannel);
		/* Get all NetworkServer of the clients */
		findNetworkServerByClients(&networkServerList, &networkClientList);
		/* Get all LocalServer of the NetworkServer */
		findLocalServerByNetworkServer(&localServerList, &networkServerList);

		/*
		 * Send message to all LocalServer
		 */
		for (std::list<LocalServer*>::iterator it = localServerList.begin(); it != localServerList.end(); ++it) {

			LocalServer *localServer = *it;

			ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(localServer);
			serverMessagePackage->setMessageType(ServerMessagePackage::TYPE_CHANNEL_MESSAGE);
			serverMessagePackage->setSender(Config::instance()->getClientName());
			serverMessagePackage->setParameter(ServerMessagePackage::CHANNEL_MESSAGE__NAME, channelName);
			serverMessagePackage->setParameter(ServerMessagePackage::CHANNEL_MESSAGE__SENDER, senderName);
			serverMessagePackage->setText(text);

			connectionManager->pushOutboundMessagePackageList(serverMessagePackage);

		}

		/*
		 * Send message to all LocalClients
		 */
		std::list<LocalNode*> *localClientList = connectionManager->getLocalClientList();

		for (std::list<LocalNode*>::iterator it = localClientList->begin(); it != localClientList->end(); ++it) {

			LocalClient *localClientFromList = (LocalClient*) *it;

			if (localClient != localClientFromList) {

				if (localClientFromList->getNetworkClient()->getNetworkChannel() == networkChannel) {

					/*
					 * Inform localClient
					 */
					std::string clientText;
					std::ostringstream clientTextStream;
					clientTextStream << senderName << ":> " << text;
					clientText = clientTextStream.str();

					ClientMessagePackage *clientMessagePackage = new ClientMessagePackage(localClientFromList);
					clientMessagePackage->setText(clientText);

					connectionManager->pushOutboundMessagePackageList(clientMessagePackage);

				}

			}

		}

	} else {

		/*
		 * Inform LocalClient that he's not assigned to a channel
		 */
		std::string clientText = "NETWORK:> Fail - No channel!";

		ClientMessagePackage *clientMessagePackage = new ClientMessagePackage(localClient);
		clientMessagePackage->setText(clientText);

		connectionManager->pushOutboundMessagePackageList(clientMessagePackage);

	}
}

void IBRCNetworkManager::processClientGettopicMessage(ClientMessagePackage *package) {

	/* Get required information from ClientMessagePackage */
	LocalClient *localClient = (LocalClient*) package->getLocalNode();
	NetworkClient *networkClient = localClient->getNetworkClient();
	NetworkChannel *networkChannel = networkClient->getNetworkChannel();

	std::string clientText;

	/*
	 * Get Topic of channel (if channel is available) and send it to client
	 */
	if (networkChannel) {

		std::ostringstream clientTextStream;
		clientTextStream << "NETWORK:> Topic: " << networkChannel->getTopic();
		clientText = clientTextStream.str();

	} else {
		clientText = "NETWORK:> Fail - No channel!";
	}

	ClientMessagePackage *clientMessagePackage = new ClientMessagePackage(localClient);
	clientMessagePackage->setText(clientText);

	connectionManager->pushOutboundMessagePackageList(clientMessagePackage);

}

void IBRCNetworkManager::processClientJoinMessage(ClientMessagePackage *package) {

	LocalNode *parentNode = connectionManager->getLocalParentServer();

	LocalClient *localClient = (LocalClient*) package->getLocalNode();
	NetworkClient *networkClient = localClient->getNetworkClient();

	std::string nick = networkClient->getClientName();
	std::string channel = package->getParameter(ClientMessagePackage::JOIN_CHANNEL_NAME);

	ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(parentNode);
	serverMessagePackage->setMessageType(ServerMessagePackage::TYPE_REQUEST_JOIN);
	serverMessagePackage->setParameter(ServerMessagePackage::REQUEST_JOIN__NICK, nick);
	serverMessagePackage->setParameter(ServerMessagePackage::REQUEST_JOIN__CHANNEL, channel);

	if (parentNode) {

		serverMessagePackage->setLocalNode(parentNode);
		connectionManager->pushOutboundMessagePackageList(serverMessagePackage);

	} else {

		connectionManager->pushInboundMessagePackageList(serverMessagePackage);

	}

}

void IBRCNetworkManager::processClientLeaveMessage(ClientMessagePackage *package) {

	LocalNode *parentNode = connectionManager->getLocalParentServer();

	LocalClient *localClient = (LocalClient*) package->getLocalNode();
	NetworkClient *networkClient = localClient->getNetworkClient();

	std::string nick = networkClient->getClientName();

	ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(parentNode);
	serverMessagePackage->setMessageType(ServerMessagePackage::TYPE_REQUEST_LEAVE);
	serverMessagePackage->setParameter(ServerMessagePackage::REQUEST_LEAVE__NICK, nick);

	if (parentNode) {

		serverMessagePackage->setLocalNode(parentNode);
		connectionManager->pushOutboundMessagePackageList(serverMessagePackage);

	} else {

		connectionManager->pushInboundMessagePackageList(serverMessagePackage);

	}

}

void IBRCNetworkManager::processClientListMessage(ClientMessagePackage *package) {

	LocalClient *localClient = (LocalClient*) package->getLocalNode();

	std::string clientText;
	std::ostringstream clientTextStream;

	/*
	 * Build string
	 */
	clientTextStream << "NETWORK:> Channel list:" << std::endl;

	for (std::list<NetworkChannel*>::iterator it = networkChannelList->begin(); it != networkChannelList->end(); ++it) {

		NetworkChannel *networkChannel = *it;

		clientTextStream << "NETWORK:>\t" << networkChannel->getChannelName() << std::endl;

	}

	clientText = clientTextStream.str();

	/*
	 * Inform LocalClient
	 */
	ClientMessagePackage *clientMessagePackage = new ClientMessagePackage(localClient);
	clientMessagePackage->setText(clientText);

	connectionManager->pushOutboundMessagePackageList(clientMessagePackage);

}

void IBRCNetworkManager::processClientNickMessage(ClientMessagePackage *package) {

	LocalNode *parentNode = connectionManager->getLocalParentServer();

	LocalClient *localClient = (LocalClient*) package->getLocalNode();
	NetworkClient *networkClient = localClient->getNetworkClient();

	std::string nick = networkClient->getClientName();
	std::string newNick = package->getParameter(ClientMessagePackage::NICK_REQUESTED_NICK);

	ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(parentNode);
	serverMessagePackage->setMessageType(ServerMessagePackage::TYPE_REQUEST_NICK);
	serverMessagePackage->setParameter(ServerMessagePackage::REQUEST_NICK__NICK, nick);
	serverMessagePackage->setParameter(ServerMessagePackage::REQUEST_NICK__NEW_NICK, newNick);

	if (parentNode) {

		serverMessagePackage->setLocalNode(parentNode);
		connectionManager->pushOutboundMessagePackageList(serverMessagePackage);

	} else {

		connectionManager->pushInboundMessagePackageList(serverMessagePackage);

	}

}

void IBRCNetworkManager::processClientPrivateMessage(ClientMessagePackage *package) {

	/* Get required information from ClientMessagePackage */
	LocalClient *localClient = (LocalClient*) package->getLocalNode();
	NetworkClient *networkClient = localClient->getNetworkClient();

	std::string& destinationName = package->getParameter(ClientMessagePackage::PRIVMESSAGE_NAME);
	std::string& senderName = networkClient->getClientName();
	std::string& text = package->getText();

	/* Get destination NetworkClient */
	NetworkClient *destinationNetworkClient = findNetworkClientByName(destinationName);
	/* Get destination NetworkServer */
	NetworkServer *destinationNetworkServer = destinationNetworkClient->getNetworkServer();
	/* get sender NetworkClient */
	NetworkClient *senderNetworkClient = findNetworkClientByName(senderName);

	/*
	 * Send message to destination NetworkServer if destination NetworkClient
	 * is assigned to same like sender NetworkClient
	 */
	if (destinationNetworkClient->getNetworkChannel() == senderNetworkClient->getNetworkChannel()) {

		if (destinationNetworkServer == networkServer) {

			/*
			 * Destination NetworkClient is connected to this server
			 */
			std::list<LocalNode*> *localClientList = connectionManager->getLocalClientList();

			for (std::list<LocalNode*>::iterator it = localClientList->begin(); it != localClientList->end(); ++it) {

				LocalClient *localClient = (LocalClient*) *it;

				if (localClient->getNetworkClient() == destinationNetworkClient) {

					/*
					 * Inform localClient
					 */
					std::string clientText;
					std::ostringstream clientTextStream;
					clientTextStream << senderName << " PRIVAT:> " << text;
					clientText = clientTextStream.str();

					ClientMessagePackage *clientMessagePackage = new ClientMessagePackage(localClient);
					clientMessagePackage->setText(clientText);

					connectionManager->pushOutboundMessagePackageList(clientMessagePackage);

				}

			}

		} else {

			/*
			 * Destination client is connected to a different server than the sender
			 * forward message into destination NetworkServer direction
			 */
			LocalServer *localServer = (LocalServer*) destinationNetworkServer->getLocalNode();

			ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(localServer);
			serverMessagePackage->setMessageType(ServerMessagePackage::TYPE_CHANNEL_PRIVMESSAGE);
			serverMessagePackage->setSender(Config::instance()->getClientName());
			serverMessagePackage->setParameter(ServerMessagePackage::CHANNEL_PRIVMESSAGE__SENDER, senderName);
			serverMessagePackage->setParameter(ServerMessagePackage::CHANNEL_PRIVMESSAGE__NAME, destinationName);
			serverMessagePackage->setText(text);

			connectionManager->pushOutboundMessagePackageList(serverMessagePackage);

		}

	} else {

		/*
		 * Inform localClient
		 */
		std::string clientText;
		std::ostringstream clientTextStream;
		clientTextStream << "NETWORK:> Fail - Channel mismatch";
		clientText = clientTextStream.str();

		ClientMessagePackage *clientMessagePackage = new ClientMessagePackage(localClient);
		clientMessagePackage->setText(clientText);

		connectionManager->pushOutboundMessagePackageList(clientMessagePackage);

	}

}

void IBRCNetworkManager::processClientQuitMessage(ClientMessagePackage *package) {

	/*
	 * Broadcast server unregister
	 */
	LocalNode *parentNode = connectionManager->getLocalParentServer();

	ServerMessagePackage serverMessagePackage(parentNode);
	serverMessagePackage.setMessageType(ServerMessagePackage::TYPE_SERVER_UNREGISTRATION);
	serverMessagePackage.setSender(Config::instance()->getClientName());
	serverMessagePackage.setParameter(ServerMessagePackage::SERVER_UNREGISTRATION__SERVER_NAME, Config::instance()->getClientName());

	performCompleteBroadcast(&serverMessagePackage);

	/*
	 * Find LocalNode with most peers
	 */
	std::list<LocalNode*> *localServerList = connectionManager->getLocalServerList();
	LocalNode *mostFrequentedLocalNode = 0;
	int mostFrequentedLocalNodeSize = 0;

	for (std::list<LocalNode*>::iterator it = localServerList->begin(); it != localServerList->end(); ++it) {

		LocalNode *localNode = (*it);

		if (localNode != connectionManager->getLocalParentServer()) {

			std::list<NetworkServer*> localNetworkServerList;
			findNetworkServerByLocalNode(&localNetworkServerList, localNode);

			if (mostFrequentedLocalNode == 0 || (int)localNetworkServerList.size() > mostFrequentedLocalNodeSize) {

				mostFrequentedLocalNode = localNode;
				mostFrequentedLocalNodeSize = localNetworkServerList.size();

			}
		}
	}

	LocalNode *parentLocalNode = connectionManager->getLocalParentServer();

	std::string parentAddress;
	std::string parentPort;

	if (parentLocalNode) {

		parentAddress = connectionManager->getParentAddress();

		std::stringstream ss;
		ss << connectionManager->getParentPort();
		parentPort = ss.str();

	} else {
		parentAddress = "nothing";
		parentPort = "nothing";
	}

	ServerMessagePackage *reconnectServerMessagePackage = new ServerMessagePackage(mostFrequentedLocalNode);
	reconnectServerMessagePackage->setLocalNode(mostFrequentedLocalNode);
	reconnectServerMessagePackage->setMessageType(ServerMessagePackage::TYPE_SERVER_RECONNECT);
	reconnectServerMessagePackage->setSender(networkClient->getClientName());
	reconnectServerMessagePackage->setParameter(ServerMessagePackage::SERVER_RECONNECT__SERVER_IP, parentAddress);
	reconnectServerMessagePackage->setParameter(ServerMessagePackage::SERVER_RECONNECT__SERVER_PORT, parentPort);

	connectionManager->pushOutboundMessagePackageList(reconnectServerMessagePackage);

	for (std::list<LocalNode*>::iterator it = localServerList->begin(); it != localServerList->end(); ++it) {

		LocalNode *localNode = (*it);

		if (localNode != connectionManager->getLocalParentServer() && localNode != mostFrequentedLocalNode) {

			ServerMessagePackage *reconnectServerMessagePackage = new ServerMessagePackage(mostFrequentedLocalNode);
			reconnectServerMessagePackage->setLocalNode(localNode);
			reconnectServerMessagePackage->setMessageType(ServerMessagePackage::TYPE_SERVER_RECONNECT);
			reconnectServerMessagePackage->setSender(networkClient->getClientName());
			reconnectServerMessagePackage->setParameter(ServerMessagePackage::SERVER_RECONNECT__SERVER_IP, "discover");
			reconnectServerMessagePackage->setParameter(ServerMessagePackage::SERVER_RECONNECT__SERVER_PORT, "discover");

			connectionManager->pushOutboundMessagePackageList(reconnectServerMessagePackage);

		}
	}

	ibrcNetworkManagerState = STATE_QUIT;

}

void IBRCNetworkManager::processClientSettopicMessage(ClientMessagePackage *package) {

	LocalNode *parentNode = connectionManager->getLocalParentServer();

	LocalClient *localClient = (LocalClient*) package->getLocalNode();
	NetworkClient *networkClient = localClient->getNetworkClient();
	NetworkChannel *networkChannel = networkClient->getNetworkChannel();

	if (networkChannel) {

		std::string nick = networkClient->getClientName();
		std::string topic = package->getParameter(ClientMessagePackage::SETTOPIC_CHANNEL);
		std::string channel = networkChannel->getChannelName();

		ServerMessagePackage *serverMessagePackage = new ServerMessagePackage(parentNode);
		serverMessagePackage->setMessageType(ServerMessagePackage::TYPE_REQUEST_SETTOPIC);
		serverMessagePackage->setParameter(ServerMessagePackage::REQUEST_SETTOPIC__CHANNEL, channel);
		serverMessagePackage->setParameter(ServerMessagePackage::REQUEST_SETTOPIC__NICK, nick);
		serverMessagePackage->setParameter(ServerMessagePackage::REQUEST_SETTOPIC__TOPIC, topic);

		if (parentNode) {

			serverMessagePackage->setLocalNode(parentNode);
			connectionManager->pushOutboundMessagePackageList(serverMessagePackage);

		} else {

			connectionManager->pushInboundMessagePackageList(serverMessagePackage);

		}

	} else {

		/*
		 * Inform localClient
		 */
		std::string clientText("Error - not assigned to a channel.");

		ClientMessagePackage *clientMessagePackage = new ClientMessagePackage(localClient);
		clientMessagePackage->setText(clientText);

		connectionManager->pushOutboundMessagePackageList(clientMessagePackage);

	}

}

/***********************************************************************************
 ***********************************************************************************
 *
 *
 *
 * Misc
 *
 *
 *
 ***********************************************************************************
 ***********************************************************************************/

IBRCNetworkManager::IBRCNetworkManagerState IBRCNetworkManager::getState() {
	return ibrcNetworkManagerState;
}

/**
 * Broadcasts a NetworkMessage to every LocalServer except the one it was received from
 */
void IBRCNetworkManager::performForwardBroadcast(ServerMessagePackage *package) {

	/*
	 * Send to parent
	 */
	LocalNode *senderNode = package->getLocalNode();

	/*
	 * Send to childs
	 */
	std::list<LocalNode*> *localServerList = connectionManager->getLocalServerList();

	for (std::list<LocalNode*>::iterator it = localServerList->begin(); it != localServerList->end(); ++it) {

		LocalNode *childNode = (*it);

		if (childNode != senderNode) {

			ServerMessagePackage *newPackage = new ServerMessagePackage(childNode);
			*newPackage = *package;
			newPackage->setLocalNode(childNode);
			connectionManager->pushOutboundMessagePackageList(newPackage);

		}

	}

}

/**
 * Broadcasts a NetworkMessage to every LocalServer
 */
void IBRCNetworkManager::performCompleteBroadcast(ServerMessagePackage *package) {

	/*
	 * Send to childs
	 */
	std::list<LocalNode*> *localServerList = connectionManager->getLocalServerList();

	for (std::list<LocalNode*>::iterator it = localServerList->begin(); it != localServerList->end(); ++it) {

		LocalNode *localNode = (*it);

		ServerMessagePackage *newPackage = new ServerMessagePackage(localNode);
		*newPackage = *package;
		newPackage->setLocalNode(localNode);
		connectionManager->pushOutboundMessagePackageList(newPackage);

	}

}

std::list<NetworkServer*> *IBRCNetworkManager::getNetworkServerList() {
	return networkServerList;
}

std::list<NetworkClient*> *IBRCNetworkManager::getNetworkClientList() {
	return networkClientList;
}

std::list<NetworkChannel*> *IBRCNetworkManager::getNetworkChannelList() {
	return networkChannelList;
}

/***********************************************************************************
 ***********************************************************************************
 *
 *
 *
 * List finders
 *
 *
 *
 ***********************************************************************************
 ***********************************************************************************/

NetworkClient *IBRCNetworkManager::findNetworkClientByName(std::string& name) {

	for (std::list<NetworkClient*>::iterator it = networkClientList->begin(); it != networkClientList->end(); ++it) {

		NetworkClient *networkClient = *it;

		if (!networkClient->getClientName().compare(name)) {
			return networkClient;
		}

	}

	return 0;
}

NetworkClient *IBRCNetworkManager::findNetworkClientByServer(NetworkServer *networkServer) {

	for (std::list<NetworkClient*>::iterator it = networkClientList->begin(); it != networkClientList->end(); ++it) {

		NetworkClient *networkClient = *it;

		if (networkClient->getNetworkServer() == networkServer) {
			return networkClient;
		}

	}

	return 0;

}

void *IBRCNetworkManager::findNetworkClientsByChannel(std::list<NetworkClient*> *clientList, NetworkChannel *networkChannel) {

	if (networkChannel) {

		for (std::list<NetworkClient*>::iterator it = networkClientList->begin(); it != networkClientList->end(); ++it) {

			NetworkClient *networkClient = *it;

			if (networkClient->getNetworkChannel() == networkChannel) {
				clientList->push_back(networkClient);
			}

		}

	}

	return 0;
}

NetworkChannel *IBRCNetworkManager::findNetworkChannelByName(std::string& name) {

	for (std::list<NetworkChannel*>::iterator it = networkChannelList->begin(); it != networkChannelList->end(); ++it) {

		NetworkChannel *networkChannel = *it;

		if (!networkChannel->getChannelName().compare(name)) {
			return networkChannel;
		}

	}

	return 0;
}

NetworkChannel *IBRCNetworkManager::findNetworkChannelByOwner(NetworkClient *networkClient) {

	for (std::list<NetworkChannel*>::iterator it = networkChannelList->begin(); it != networkChannelList->end(); ++it) {

		NetworkChannel *networkChannel = *it;

		if (networkChannel->getChannelOwner() == networkClient) {
			return networkChannel;
		}

	}

	return 0;

}

void *IBRCNetworkManager::findNetworkChannelByOwner(std::list<NetworkChannel*> *channelList, NetworkClient *networkClient) {

	for (std::list<NetworkChannel*>::iterator it = networkChannelList->begin(); it != networkChannelList->end(); ++it) {

		NetworkChannel *networkChannel = *it;

		if (networkChannel->getChannelOwner() == networkClient) {
			channelList->push_back(networkChannel);
		}

	}

	return 0;
}

void *IBRCNetworkManager::findNetworkServerByLocalNode(std::list<NetworkServer*> *serverList, LocalNode *localNode) {

	for (std::list<NetworkServer*>::iterator it = networkServerList->begin(); it != networkServerList->end(); ++it) {

		NetworkServer *networkServer = *it;

		if (networkServer->getLocalNode() == localNode) {
			serverList->push_back(networkServer);
		}

	}

	return 0;

}

NetworkServer *IBRCNetworkManager::findNetworkServerByName(std::string& name) {

	for (std::list<NetworkServer*>::iterator it = networkServerList->begin(); it != networkServerList->end(); ++it) {

		NetworkServer *networkServer = *it;

		if (!networkServer->getServerName().compare(name)) {
			return networkServer;
		}

	}

	return 0;
}

void *IBRCNetworkManager::findNetworkServerByClients(std::list<NetworkServer*> *serverList, std::list<NetworkClient*> *clientList) {

	for (std::list<NetworkClient*>::iterator it = clientList->begin(); it != clientList->end(); ++it) {

		NetworkClient *networkClient = *it;
		NetworkServer *networkServer = networkClient->getNetworkServer();

		if (networkServer) {

			bool serverFoundInList = false;

			for (std::list<NetworkServer*>::iterator it = serverList->begin(); it != serverList->end(); ++it) {

				if (*it == networkServer) {
					serverFoundInList = true;
					break;
				}

			}

			if (!serverFoundInList) {
				serverList->push_back(networkServer);
			}

		}

	}

	return 0;

}

void *IBRCNetworkManager::findLocalServerByNetworkServer(std::list<LocalServer*> *localServerList, std::list<NetworkServer*> *networkServerList) {

	for (std::list<NetworkServer*>::iterator it = networkServerList->begin(); it != networkServerList->end(); ++it) {

		NetworkServer *networkServer = *it;
		LocalServer *localServer = (LocalServer*) networkServer->getLocalNode();

		if (localServer) {

			bool serverFoundInList = false;

			for (std::list<LocalServer*>::iterator it = localServerList->begin(); it != localServerList->end(); ++it) {

				if (*it == localServer) {
					serverFoundInList = true;
					break;
				}

			}

			if (!serverFoundInList) {
				localServerList->push_back(localServer);
			}

		}

	}

	return 0;
}

